﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MyTRCP.Common.Codecs.Sbc;
using MyTRCP.Common.Models;

namespace MyTRCP.Common.Services
{
    public class AudioConvertService : BackgroundService
    {
        private readonly ILogger<AudioConvertService> _logger;

        private readonly BlockingQueue<byte[]> _audioPayloadRecvQueue;
        private readonly BlockingQueue<byte[]> _audioPayloadSendQueue;

        private readonly BlockingQueue<byte[]> _remoteAudioQueue;
        private readonly BlockingQueue<byte[]> _myAudioQueue;
        private readonly BlockingQueue<byte[]> _voicePcmQueue;
        private readonly SbcDecoder _sbcDecoder;
        private readonly SbcEncoder _sbcEncoder;
        private readonly byte[] _uncompleteVoicePayloadBuf;
        private int _bufPos;

        public AudioConvertService(ILogger<AudioConvertService> logger, IConfigService ics, DevAudioLinkService idals, MyProtocolClientSideService impcs)
        {
            logger.LogDebug($"AudioConvertService creating...");
            _logger = logger;
            if (ics is ClientConfigService ccs && ccs.ClientMode == ClientModeEnum.Remote)
            {
                _audioPayloadRecvQueue = impcs.AudioRxQueue;
                _audioPayloadSendQueue = impcs.AudioTxQueue;
            }
            else
            {
                _audioPayloadRecvQueue = idals.PayloadRecvQueue;
                _audioPayloadSendQueue = idals.PayloadSendQueue;
            }
            _remoteAudioQueue = new BlockingQueue<byte[]>();
            _myAudioQueue = new BlockingQueue<byte[]>();
            _voicePcmQueue = new BlockingQueue<byte[]>();
            _sbcDecoder = new SbcDecoder(false);
            _sbcEncoder = new SbcEncoder(false);
            _uncompleteVoicePayloadBuf = new byte[512];
            _bufPos = 0;
        }
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            try
            {
                Task enc = Task.Run(() => AudioEncodeThreadMethod(stoppingToken), stoppingToken);
                Task dec = Task.Run(() => AudioDecodeThreadMethod(stoppingToken), stoppingToken);
                await Task.WhenAll(enc, dec);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "音频转换服务启动时出现异常");
            }
        }
        public BlockingQueue<byte[]> RemoteAudioRxQueue
        {
            get
            {
                return _remoteAudioQueue;
            }
        }
        public BlockingQueue<byte[]> MyAudioRxQueue
        {
            get
            {
                return _myAudioQueue;
            }
        }
        public BlockingQueue<byte[]> VoicePcmTxQueue
        {
            get
            {
                return _voicePcmQueue;
            }
        }
        private void SendVoiceFrame(byte[] voicePcmByteData)
        {
            if (voicePcmByteData != null)
            {
                if (voicePcmByteData.Length > 0)
                {
                    int sbcLen = 0;
                    int pcmLen = 0;
                    int pcmPos = 0;
                    int pcmBufLen = voicePcmByteData.Length;
                    do
                    {
                        pcmLen = _sbcEncoder.SbcEncode(voicePcmByteData, pcmPos, voicePcmByteData.Length, _uncompleteVoicePayloadBuf, _bufPos, _uncompleteVoicePayloadBuf.Length, ref sbcLen);
                        pcmPos += pcmLen;
                        _bufPos += sbcLen;

                    } while (pcmLen > 0);
                    if (_bufPos >= 308)
                    {
                        AudioPayload ap = new AudioPayload(AudioPayloadTypeEnum.Normal, _uncompleteVoicePayloadBuf, 0, _bufPos);
                        _bufPos = 0;
                        _audioPayloadSendQueue.Add(AudioPayload.Pack(ap));
                    }
                }
                else if (_bufPos > 0)
                {
                    AudioPayload ap = new AudioPayload(AudioPayloadTypeEnum.Normal, _uncompleteVoicePayloadBuf, 0, _bufPos);
                    _bufPos = 0;
                    _audioPayloadSendQueue.Add(AudioPayload.Pack(ap));
                }
            }
        }
        private void AudioEncodeThreadMethod(CancellationToken stoppingToken)
        {
            _logger.LogInformation($"音频编码线程开始");
            try
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    try
                    {
                        byte[] section = _voicePcmQueue.Take();
                        if (section.Length == 1)
                            continue;
                        SendVoiceFrame(section);
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation("AudioEncodeThreadMethod in-loop Received OperationCanceledException,break.");
                        break;
                    }
                    catch (Exception e)
                    {
                        _logger.LogError(e, $"AudioEncodeThreadMethod in-loop Exception:{e.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"AudioEncodeThreadMethod Exception:{ex.Message}");
            }
            _logger.LogInformation($"音频编码线程结束");
        }
        private void AudioDecodeThreadMethod(CancellationToken stoppingToken)
        {
            _logger.LogInformation($"音频解码线程开始");
            try
            {
                bool remoteVoiceSpeaking = false;
                bool myVoiceSpeaking = false;
                byte[] pcmBuf = new byte[2048];
                byte[] pcm;
                int inPos, outPos, pcmLen, frameLen;
                while (!stoppingToken.IsCancellationRequested)
                {
                    try
                    {
                        byte[] data = _audioPayloadRecvQueue.Take();
                        AudioPayload ap = AudioPayload.Unpack(data, 0, data.Length);
                        switch (ap.PayloadType)
                        {
                            case AudioPayloadTypeEnum.Normal:
                                if (!remoteVoiceSpeaking)
                                {
                                    remoteVoiceSpeaking = true;
                                    _remoteAudioQueue.Add([0]);
                                }
                                inPos = 0;
                                outPos = 0;
                                pcmLen = 0;
                                do
                                {
                                    frameLen = _sbcDecoder.SbcDecode(ap.Datas, inPos, ap.Datas.Length, pcmBuf, outPos, pcmBuf.Length, ref pcmLen);
                                    if (frameLen > 0)
                                    {
                                        inPos += frameLen;
                                        outPos += pcmLen;
                                    }
                                }
                                while (frameLen > 0);
                                pcm = new byte[outPos];
                                Array.Copy(pcmBuf, pcm, outPos);
                                _remoteAudioQueue.Add(pcm);
                                break;
                            case AudioPayloadTypeEnum.MyVoice:
                                if (!myVoiceSpeaking)
                                {
                                    myVoiceSpeaking = true;
                                    _remoteAudioQueue.Add([9]);
                                }
                                inPos = 0;
                                outPos = 0;
                                pcmLen = 0;
                                do
                                {
                                    frameLen = _sbcDecoder.SbcDecode(ap.Datas, inPos, ap.Datas.Length, pcmBuf, outPos, pcmBuf.Length, ref pcmLen);
                                    if (frameLen > 0)
                                    {
                                        inPos += frameLen;
                                        outPos += pcmLen;
                                    }
                                }
                                while (frameLen > 0);
                                pcm = new byte[outPos];
                                Array.Copy(pcmBuf, pcm, outPos);
                                _myAudioQueue.Add(pcm);
                                break;
                            case AudioPayloadTypeEnum.Cmd:
                                switch (ap.Datas[1])
                                {
                                    case 0x04:
                                        myVoiceSpeaking = false;
                                        _myAudioQueue.Add([]);
                                        break;
                                    case 0x01:
                                        remoteVoiceSpeaking = false;
                                        _remoteAudioQueue.Add([]);
                                        break;
                                    default:
                                        if (ap.Datas.Length > 7)
                                            _logger.LogDebug($"未知命令A={ap.Datas[0]:x2}{ap.Datas[1]:x2},B={ap.Datas[2]:x2}{ap.Datas[3]:x2},C={ap.Datas[4]:x2}{ap.Datas[5]:x2},D={ap.Datas[6]:x2}{ap.Datas[7]:x2}");
                                        else if (ap.Datas.Length > 1)
                                            _logger.LogDebug($"未知命令A={ap.Datas[0]:x2}{ap.Datas[1]:x2}");
                                        else
                                            _logger.LogDebug($"未知命令");
                                        break;
                                }
                                break;
                            default:
                                _logger.LogDebug($"未知类型{ap.PayloadType}，数据为:{Tools.ByteArray2HexString(ap.Datas)}");
                                break;
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation("AudioConvertThreadMethod in-loop Received OperationCanceledException,break.");
                        break;
                    }
                    catch (Exception e)
                    {
                        _logger.LogError(e, $"AudioConvertThreadMethod in-loop Exception:{e.Message}");
                    }
                }
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"AudioConvertThreadMethod Exception:{e.Message}");
            }
            _logger.LogInformation($"音频解码线程结束");
        }
        public override Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogDebug("startasync called");
            return base.StartAsync(cancellationToken);
        }
        public override Task StopAsync(CancellationToken cancellationToken)
        {
            _audioPayloadRecvQueue.CancelTake();
            _voicePcmQueue.CancelTake();
            return base.StopAsync(cancellationToken);
        }
    }
}
